package com.sgidi.einvoice.service;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.sgidi.einvoice.models.Invoice;
import org.apache.commons.codec.binary.Base64;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class PdfQrExtractor {
    public static Invoice extract(File file) throws IOException {
        PDDocument document = PDDocument.load(file);
        try {
            Result finalRet = null;
            for (PDPage page : document.getPages()) {
                PDResources pdResources = page.getResources();
                finalRet = processXObject(pdResources);
            }
            if (finalRet == null) return null;
            String[] arr = finalRet.getText().split(",");
            if (arr.length < 6) return null;
            Invoice invoice = parseFromArray(arr);
            invoice.setBase64Png(getBase64Image(document));
            SetTotalAmountForOldInvoice(document, invoice);
            document.close();
            return invoice;
        } catch (Exception e) {
            document.close();
            return null;
        }
    }

    private static Result processXObject(PDResources pdResources) throws IOException {
        for (COSName cosName : pdResources.getXObjectNames()) {
            PDXObject xObject = pdResources.getXObject(cosName);

            // 如果是图片，尝试解码二维码
            if (xObject instanceof PDImageXObject) {
                PDImageXObject image = (PDImageXObject) xObject;
                Result ret = decodeQRCode(image.getImage());
                if (ret != null) {
                    return ret;
                }
            }

            // 如果是表单 XObject，递归检查
            if (xObject instanceof PDFormXObject) {
                PDFormXObject form = (PDFormXObject) xObject;
                Result ret = processXObject(form.getResources());
                if (ret != null) {
                    return ret; // 找到二维码后返回
                }
            }
        }
        return null;
    }

    /*从字符串数组里按照固定的排列给Invoice赋值*/
    private static Invoice parseFromArray(String[] arr) {
        // 01,31,,23312000000149321598,750.00,20231202,,5038 新发票
        // 01,10,050002100311,78881306,317.81,20230505,44035891270363352468,BC4B, 老发票
        Invoice invoice = new Invoice();
        invoice.setCode(arr[2].trim());
        invoice.setNumber(arr[3].trim());
        double value = new Double(arr[4]);
        if (arr[2] != null && !arr[2].trim().isEmpty()) {
            BigDecimal d = new BigDecimal(value + value * 0.13);
            invoice.setTotalAmount(d.setScale(2, RoundingMode.HALF_UP));
        } else {
            invoice.setTotalAmount(new BigDecimal(arr[4]));
        }
        return invoice;
    }

    private static Result decodeQRCode(BufferedImage bufferedImage) {
        try {
            LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
            BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

            Reader reader = new QRCodeReader();
            return reader.decode(binaryBitmap);
        } catch (Exception e) {
            // 如果没有找到二维码，则可能会抛出异常
            return null;
        }
    }

    private static String getBase64Image(PDDocument doc) throws IOException {
        PDFRenderer renderer = new PDFRenderer(doc);
        BufferedImage image = renderer.renderImageWithDPI(0, 150);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", byteArrayOutputStream);
        return "data:image/png;base64," + new String(Base64.encodeBase64(byteArrayOutputStream.toByteArray()));
    }

    private static void SetTotalAmountForOldInvoice(PDDocument doc, Invoice invoice) throws IOException {
        if (invoice.isElectronic()) return;
        double value = PdfAmountExtractor.extract(doc);
        if (value == 0) return;
        BigDecimal decimal = new BigDecimal(value);
        invoice.setTotalAmount(decimal);
    }
}
